using System;
using System.Linq;
using System.Reflection;
using Jink.Core.Handlers.Annotations;
using Jink.Core.Handlers.Bind;
using Jink.Protocol.Core.Verbs;
using XenoGears.Collections.Dictionaries;
using XenoGears.Functional;
using XenoGears.Reflection.Attributes;
using XenoGears.Reflection.Shortcuts;

namespace Jink.Core.Handlers
{
    public class Handler
    {
        public MethodInfo Code { get; private set; }
        public String UriTemplate { get; private set; }
        public Verb Verb { get; private set; }
        public ReadOnlyDictionary<String, Type> Args { get; private set; }

        public Handler(MethodInfo code, HandlerAttribute handler, HandlesAttribute handles)
        {
            Code = code;
            UriTemplate = ConcatUrls(
                handler is ResourceAttribute ? ((ResourceAttribute)handler).Resource : null,
                handles.UriTemplate);
            Verb = handles.Verb;

            var args = code.GetParameters().ToDictionary(p => p.Name, p => p.ParameterType);
            if (!code.IsStatic)
            {
                var fields = code.DeclaringType.GetFields(BF.AllInstance).Where(f => f.HasAttr<BindAttribute>());
                args.AddElements(fields.ToDictionary(f => f.Name, f => f.FieldType));

                var props = code.DeclaringType.GetProperties(BF.AllInstance).Where(f => f.HasAttr<BindAttribute>());
                args.AddElements(props.ToDictionary(p => p.Name, f => f.PropertyType));
            }

            Args = args.ToReadOnly();
        }

        private String ConcatUrls(String url1, String url2)
        {
            if (url1.IsNullOrEmpty()) return url2;
            if (url2.IsNullOrEmpty()) return url1;
            if (url1.EndsWith("/") && url2.StartsWith("/")) return url1 + url2.Slice(1);
            return url1 + url2;
        }

        public BindResult Match(CoreRequest request)
        {
            return new BindResult(request, this);
        }
    }
}